//
//  OpenGLHelper.c
//  MacTest
//
//  Created by yuanxc on 2024/5/25.
//

#include "OpenGLHelper.h"
#include <stdio.h>
#include <stdlib.h>

// 多行字符串定义
const GLchar* vertexShaderSource =
"#version 410 core\n"
"void main() {\n"
"    const vec4 vertices[3] = vec4[3](vec4( 0.9, -0.9, 0.5, 1.0),\n"
"                                     vec4(-0.9, -0.9, 0.5, 1.0),\n"
"                                     vec4( 0.9,  0.9, 0.5, 1.0));\n"
"    gl_Position = vertices[gl_VertexID];\n"
"}\n";

const GLchar* tControlShaderSource =
"#version 410 core\n"
"layout (vertices = 3) out;\n"
"void main() {\n"
"    if (gl_InvocationID == 0) {\n"
"        gl_TessLevelInner[0] = 5.0;\n"
"        gl_TessLevelOuter[0] = 5.0;\n"
"        gl_TessLevelOuter[1] = 5.0;\n"
"        gl_TessLevelOuter[2] = 5.0;\n"
"    }\n"
"    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
"}\n";

const GLchar* tEvaluationShaderSource =
"#version 410 core\n"
"layout (triangles, equal_spacing, cw) in;\n"
"void main() {\n"
"    vec4 firstVertex = gl_in[0].gl_Position;\n"
"    vec4 secondVertex = gl_in[1].gl_Position;\n"
"    vec4 thirdVertex = gl_in[2].gl_Position;\n"
"    gl_Position = (gl_TessCoord.x * firstVertex + gl_TessCoord.y * secondVertex + gl_TessCoord.z * thirdVertex);\n"
"}\n";

const GLchar* fragmentShaderSource =
"#version 410 core\n"
"out vec4 color;\n"
"void main() {\n"
"    color = vec4(1.0, 0.0, 1.0, 1.0);\n"
"}\n";

void printShaderLog(GLuint shader) {
    GLint log_length = 0;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);

    if (log_length > 0) {
        char* log = (char*)malloc(log_length);
        GLsizei written;
        glGetShaderInfoLog(shader, log_length, &written, log);
        printf("Shader log: \n%s", log);
        free(log);
    }
}

GLuint compileShader(const char* shaderSource, GLenum shaderType) {
    GLuint shader = glCreateShader(shaderType);
    glShaderSource(shader, 1, &shaderSource, NULL);
    glCompileShader(shader);

    GLint success;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
    if (!success) {
        printf("ERROR: Shader compilation failed!\n");
        printShaderLog(shader);
        glDeleteShader(shader);
        exit(EXIT_FAILURE);
    }

    return shader;
}

void setupOpenGLShaders(GLuint *program, GLuint *vertexArray) {
    GLuint vertexShader = compileShader(vertexShaderSource, GL_VERTEX_SHADER);
    GLuint tControlShader = compileShader(tControlShaderSource, GL_TESS_CONTROL_SHADER);
    GLuint tEvaluationShader = compileShader(tEvaluationShaderSource, GL_TESS_EVALUATION_SHADER);
    GLuint fragmentShader = compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER);

    *program = glCreateProgram();
    glAttachShader(*program, vertexShader);
    glAttachShader(*program, tControlShader);
    glAttachShader(*program, tEvaluationShader);
    glAttachShader(*program, fragmentShader);
    glLinkProgram(*program);

    GLint success;
    glGetProgramiv(*program, GL_LINK_STATUS, &success);
    if (!success) {
        printf("ERROR: Program linking failed!\n");
        GLchar log[1024] = {0};
        glGetProgramInfoLog(*program, 1024, NULL, log);
        printf("Program link log: \n%s", log);
        glDeleteProgram(*program);
        exit(EXIT_FAILURE);
    }

    glDeleteShader(vertexShader);
    glDeleteShader(tControlShader);
    glDeleteShader(tEvaluationShader);
    glDeleteShader(fragmentShader);

    glGenVertexArrays(1, vertexArray);
    glBindVertexArray(*vertexArray);
}

void render(GLuint program) {
    GLfloat color[] = {0.3f, 0.3f, 0.3f, 1.0f};
    glClearBufferfv(GL_COLOR, 0, color);

    glUseProgram(program);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glDrawArrays(GL_PATCHES, 0, 3);

    glFlush();
}
